//  When I wrote this code, only God and I knew what it was. Now, only God knows!
/*
 * @Description:
 * @Author: yjp
 * @Date: 2020-10-26
 * @LastEditTime: 2021-02-07 08:54:07
 * @FilePath: /config/webpack/util.js
 */
const glob = require('glob');
const fs = require('fs');
const chalk = require('chalk');
const archiver = require('archiver');
const path = require('path');
const copyDir = require('copy-dir');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const Inquirer = require('inquirer');
// const { resolve } = require('path');
// const { reject } = require('core-js/fn/promise');
const config = require('./../config.js');

function HashUtil() {
}
HashUtil.prototype.apply = function (compiler) {
  compiler.plugin('emit', (compilation, cb) => {
    // see https://webpack.js.org/api/stats/

    const { entrypoints } = compilation;
    const pluginNames = Array.from(entrypoints.keys());

    const assets = {
      css: 'cssEntry',
      js: 'jsEntry',
    };

    for (let i = 0; i < pluginNames.length; i++) {
      const pluginName = pluginNames[i];
      const entryFiles = entrypoints.get(pluginName).getFiles();
      const entryPathByArr = compilation.options.entry[pluginName].split('/');
      const entryName = entryPathByArr[entryPathByArr.length - 2];
      const ret = {};
      entryFiles.forEach((path) => {
        Object.assign(ret, getOutputPath(path));
      });
      writeFile(entryName, ret);
    }

    function getOutputPath(path) {
      const ret = {};
      const sp = path.split('.');
      const ext = sp[sp.length - 1];
      if (assets.hasOwnProperty(ext)) {
        ret[assets[ext]] = path;
      }
      return ret;
    }

    function writeFile(path, fileInfo) {
      const configPath = `./src/app/views/${path}/config.json`;
      try {
        const configBuffer = fs.readFileSync(configPath);
        let config = JSON.parse(configBuffer.toString());// 将字符串转换为json对象
        config = Object.assign(config, fileInfo);
        fs.writeFileSync(configPath, JSON.stringify(config));
        console.log(chalk.green(`${path}entry信息输出成功`));
      } catch (e) {
        console.error(`${configPath}文件不存在`);
      }
    }

    cb();
  });
};
const utils = {
  entryFile: 'index.js',
  /**
   * 复制公共资源
   */
  copyComment() {
    const commentAsset = path.resolve(__dirname, '../../', 'appStatic/assets');
    if (fs.existsSync(commentAsset)) {
      const entrys = config.entry || [];
      entrys.forEach((entry) => {
        copyDir(commentAsset, path.resolve(__dirname, '../../', `appStatic/${entry}/assets`), () => {
          console.log('公共资源复制成功！');
        });
      });
    }
  },
  /**
   * 资源文件目录
   * @param {String} url 资源路径
   * @param {String} type 资源类型
   * @returns {String}
   */
  buildAssetsUrl(url, type) {
    let name = path.join(`assets/${type}/[name].[hash:7].[ext]`); // 默认资源目录
    // 业务模块
    const dirs = [{
      dir: path.join('src/app/views/'),
    }];
    dirs.forEach((dir) => {
      if (url.indexOf(dir.dir) !== -1) {
        let folder = url.substring(url.indexOf(dir.dir)).replace(dir.dir, '');
        // folder = folder.substring(0,folder.lastIndexOf('/'));
        // eslint-disable-next-line prefer-destructuring
        folder = folder.split(path.join('/'))[0];
        const pluginCode = this.mapping[folder];
        const pluginConfig = this.entryConfigs[pluginCode];
        // name = dev ? path.join(`${pluginCode}/${name}`) : path.join(`${pluginCode}/${pluginConfig.version}/${name}`);
        name = path.join(`./${pluginCode}/${name}?version=${pluginConfig.version}`);
        return false;
      }
      return true;
    });
    return `./${name}`;
  },
  /**
   * 构建输出文件路径
   * @param {*} file 文件
   * @param {*} type 类型：js|css
   * @returns {String}
   */
  buildFileName(file, type) {
    const names = file.chunk.name.split('/');
    const folder = names[0];
    names.shift();
    names.push('index');
    const filename = names.join('_');
    const url = `${folder}/${filename}.[contenthash:7].${type}`;
    return url;
  },
  /**
   * 处理引用链接
   * @param {*} url //资源路径
   * @returns {String}
   */
  buildPublicPath(url) {
    const paths = url.split(path.join('/'));
    paths.splice(0, 1, '.');
    url = paths.join('/');
    return url;
  },
  /**
   * 自动构建入口文件
   * @returns {Object}
   */
  buildJEEntry() {
    // 动态读取入口
    const entrys = {};
    const entryInfo = config.entryInfo.je;
    console.log('dongt', config);
    config.jeentry.forEach((entry) => {
      const entryFile = entryInfo[entry];
      if (entryFile) {
        const file = path.resolve(__dirname, '../../', entryFile);
        entrys[entry] = file;
      }
    });
    return entrys;
  },
  /**
   * 自动构建入口文件
   * @returns {Object}
   */
  buildEntry() {
    if (this.entrys) {
      return this.entrys;
    }
    const me = this;
    // 动态读取入口
    const files = glob.sync(`./src/app/views/**/${me.entryFile}`);
    const entrys = {};
    const entry = config.entry || [];
    files.forEach((f) => {
      let name = f.replace('./src/app/views/', '').replace(`/${me.entryFile}`, '');
      if (entry.length === 0 || entry.indexOf(name.split('/')[0]) !== -1) {
        try {
          const configFile = `${f.slice(0, f.lastIndexOf('/'))}/config.json`;
          let projectConfig = JSON.parse(fs.readFileSync(configFile).toString());
          // 如果不存在功能编码，说明是在打包子页面，需要将路径的第一级别替换为上级的功能编码，然后进行打包
          // console.log('1111',configFile, projectConfig);
          if (!projectConfig.pluginCode) {
            const nameArr = name.split('/');
            const parentDirName = nameArr[0];
            const parentConfigFile = `./src/app/views/${parentDirName}/config.json`;
            const projectConfig = JSON.parse(fs.readFileSync(parentConfigFile).toString());
            nameArr[0] = projectConfig.pluginCode;
            name = nameArr.join('/');
          }

          entrys[projectConfig.pluginCode || name] = f;
          console.log(entrys)
          
        } catch (e) {
          console.log(`${name}插件未获取到config.json配置文件，或配置文件异常，请检查配置信息`);
        }
      }
    });
    
    this.entrys = entrys;
    return entrys;
  },
  /**
   * 获取当前入口文件的所有配置信息
   */
  buildEntryConfigs() {
    if (this.entryConfigs) {
      return this.entryConfigs;
    }
    if (!this.entrys) {
      this.buildEntry();
    }
    const ret = {};
    Object.entries(this.entrys).forEach(([k, v]) => {
      const configPath = v.replace('index.js', 'config.json');
      ret[k] = this.getConfig(configPath);
    });
    this.entryConfigs = ret;
    return ret;
  },
  /**
   * 构建html
   * @param {Object} entrys 入口
   * @returns {Array}
   */
  buildPages(entrys) {
    const me = this;

    const pages = [];
    const tpl = './config-app/template/template.html';
    const indexTpl = './config-app/template/index.html';
    // const appFolder = 'app/';
    for (const entry in entrys) {
      const _path = entrys[entry];
      const configFile = _path.replace(`/${me.entryFile}`, '/config.json');
      let _config = {};
      if (fs.existsSync(configFile)) {
        _config = JSON.parse(fs.readFileSync(configFile).toString());
      }
      _config.include = _config.include || [];
      if (_config.include.indexOf('vue') == -1) {
        _config.include.push('vue');
      }
      _config.include.push('resetcss', 'flexible');
      _config.files = _config.files || {};
      const names = entry.split('/');
      const folder = names[0];
      names.shift();
      names.push('index');
      const filename = `${folder}/${names.join('_')}.html`;

      /* const relativeRoot = [];
      (appFolder + entry).split('/').forEach(() => {
        relativeRoot.push('..');
      }); */
      pages.push(new HtmlWebpackPlugin({
        template: tpl,
        inject: false,
        filename,
        config: _config,
        chunks: [entry],
        minify: false, // 不压缩
        RELATIVE_ROOT: '../..', // relativeRoot.join('/'),
      }));
    }
    if (process.env.NODE_ENV === 'development') { // 开发
      pages.push(new HtmlWebpackPlugin({
        template: indexTpl,
        inject: false,
        minify: false, // 不压缩
        indexConfig: { // 首页配置信息
          entry: this.mapping[config.entry[0]],
          username: config.username,
          password: config.password,
          proxyServerUrl: config.proxyServerUrl,
          header: config.header,
          socket: config.socket,
        },
        filename: path.resolve(__dirname, '../../', 'index.html'),
      }));
    }

    return pages;
  },
  /**
   * 生成插件包
   */
  zip() {
    const _this = this;
    const appRoot = path.resolve(__dirname, '../../', 'appStatic');
    const entry = config.entry || [];
    entry.forEach((fileName) => {
      // // 插件
      const { mapping } = _this;
      const _pluginName = mapping[fileName];
      const pluginPath = path.join(`${appRoot}/${fileName}.zip`);
      const plugin = fs.createWriteStream(pluginPath);
      // 插件的zip包
      const pluginArchive = archiver('zip', {
        zlib: {
          level: 9,
        },
      });
      plugin.on('close', () => {
        _this.uploadUpgradeFile.call(_this, appRoot, fileName, pluginPath);
      });
      pluginArchive.pipe(plugin);
      pluginArchive.directory(path.join(`appStatic/${_pluginName}/`), false);
      pluginArchive.finalize();
    });
  },
  /**
   * 创建插件名称与插件目录的映射关系
   */
  createPluginNameMapping() {
    const mapping = {};
    const files = this.buildEntry();
    Object.entries(files).forEach(([pluginCode, filePath]) => {
      const config = this.getConfig(filePath.replace('index.js', 'config.json'));
      const filePathArr = filePath.split('/');
      const fileName = filePathArr[filePathArr.length - 2];
      mapping[config.pluginCode] = fileName;
      mapping[fileName] = config.pluginCode;
    });
    console.log(chalk.blue('插件编码映射成功！~~'));
    return mapping;
  },

  /**
   * 获取配置信息
   * @param pluginConfigPath
   * @return {{}}
   */
  getConfig(pluginConfigPath) {
    // 读取插件本地的配置信息
    let pluginConfig = {};
    if (fs.existsSync(pluginConfigPath)) {
      const pluginBuffer = fs.readFileSync(pluginConfigPath);
      pluginConfig = JSON.parse(pluginBuffer.toString());
    }
    return pluginConfig;
  },
  // 选中插件的版本自动升级
  versionAdd(path) {
    const configPath = `./src/app/views/${path}/config.json`;
    try {
      const configBuffer = fs.readFileSync(configPath);
      const config_version = JSON.parse(configBuffer.toString());// 将字符串转换为json对象
      config_version.version = parseInt(config_version.version) + 1;
      fs.writeFileSync(configPath, JSON.stringify(config_version));
      console.log(chalk.green(`${path}插件版本version自动升级为${config_version.version}`));
    } catch (e) {
      console.error(`${configPath}文件不存在`);
    }
  },
  setConfig(path, fileInfo) {
    const configPath = `./src/app/views/${path}/config.json`;
    try {
      const configBuffer = fs.readFileSync(configPath);
      let config = JSON.parse(configBuffer.toString());// 将字符串转换为json对象
      config = Object.assign(config, fileInfo);
      fs.writeFileSync(configPath, JSON.stringify(config));
      console.log(chalk.green(`${path}插件支持h5配置修改成功`));
    } catch (e) {
      console.error(`${configPath}文件不存在`);
    }
  },
  /**
   * 压缩正式的升级包的代码
   * @param {string} appRoot  项目入口
   * @param {*} fileName 文件名称
   * @param {*} pluginPath  插件地址
   */
  uploadUpgradeFile(appRoot, fileName, pluginPath) {
    const me = this;
    const output = fs.createWriteStream(path.join(`${appRoot}/JE-PLUGIN-${fileName.toLocaleUpperCase()}.zip`));
    output.on('close', () => {
      fs.unlink(pluginPath, (params) => {
        if (params) {
          console.log(error);
          return false;
        }
        console.log(`JE-PLUGIN-${fileName.toLocaleUpperCase()}.zip 删除成功`);
      });
      console.log(`${chalk.green(`${fileName}.zip`)} ，${(archive.pointer() / 1024 / 1024).toFixed(4)}M`);
    });
    const archive = archiver('zip', {
      zlib: {
        level: 9,
      }, // Sets the compression level.
    });
    // good practice to catch this error explicitly
    archive.on('error', (err) => {
      throw err;
    });
    // 打包插件升级包
    archive.pipe(output);
    archive.append(me.getUpgradeInfo(fileName), {
      name: 'config.json',
    });
    archive.append(fs.createReadStream(pluginPath), {
      name: 'plugin.zip',
    });
    archive.finalize();
  },

  /**
   * 驼峰转-
   * @param {String} name 驼峰命名
   * @returns {String}
   */
  toLine(name) {
    return name.replace(/([A-Z])/g, '-$1').toLowerCase();
  },

  /**
   * 获取升级包信息
   */
  getUpgradeInfo(fileName) {
    // 源码目录
    const viewPath = path.resolve(__dirname, '../../src/app/views');
    // 源码目录
    const configFilePath = path.resolve(viewPath, fileName);
    // 读取到配置文件
    const config = JSON.parse(fs.readFileSync(`${configFilePath}/config.json`).toString());
    // config.fileName = fileName;
    delete config.include;
    delete config.debug;
    return JSON.stringify(config);
  },
  /**
   * @description: 询问插件是否支持h5
   * @param :
   * @return {*}
   */
  addSupportH5Config() {
    const entrys = config.entry || [];
    if (process.env.NODE_ENV === 'development') {
      entrys.forEach((entry) => {
        this.setConfig(entry, { supportH5: true, debug: true });
        console.log(chalk.green(`${entry}插件支持h5调试功能`));
      });
      return Promise.resolve();
    }
    // 只要打包build就要让版本加一
    // TODO 关闭版本自动增加
    // entrys.forEach((res) => {
    //   this.versionAdd(res); // 控制版本加一
    // });
    // const choices=this.mapping.entrys.map(item=>{return{name:item}})
    // 当前选择打包的插件
    return new Promise((resolve, reject) => {
      Inquirer.prompt({
        type: 'checkbox',
        name: 'supportH5Name',
        message: `\n请选择您打包的【${entrys}】插件是否支持h5功能`,
        prefix: '',
        suffix: ':(【多选 )',
        choices: entrys,
      }).then((res) => {
        entrys.forEach((name) => {
          if (res.supportH5Name.includes(name)) {
            this.setConfig(name, { supportH5: true });
          } else {
            this.setConfig(name, { supportH5: false });
          }
        });
        return true;
      }).then((data) => {
        Inquirer.prompt({
          type: 'checkbox',
          name: 'supportDebug',
          message: `\n请选择您打包的【${entrys}】插件是否支持debug调试功能`,
          prefix: '',
          suffix: ':(【多选 )',
          choices: entrys,
        }).then((res) => {
          entrys.forEach((name) => {
            if (res.supportDebug.includes(name)) {
              this.setConfig(name, { debug: true });
            } else {
              this.setConfig(name, { debug: false });
            }
          });
          resolve();
        });
      });
    });
  },

  /**
   * 获取webpack打包的hash值
   * @param options
   * @constructor
   */
  HashUtil,
};
utils.mapping = utils.createPluginNameMapping();
module.exports = utils;
